<?php

/* 
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
namespace org\copperforest\oak\resources\configuration;
use \org\copperforest\oak\http\exceptions as ex;

class Configuration extends AbstractConfiguration
{

    protected $isRoot = true;
    protected $collection = false;
    protected $goundTemplate;
    protected $accept;
    static protected $contador=0;
    
    public function __construct( &$config )
    {
        $this->config = $config;
    }
    
    
    public function fetch( $relReference, $isRoot = false )
    {
        
        
        if( $relReference[ 0 ] == '/' ){
            $relReference = substr( $relReference, 1 );
        }
            
        $resourcesArray = explode( '/', $relReference );

        $idCountdown = 0;
        $numCountdown =  count( $resourcesArray );
        $foundResource = false;

        if( $this->collection ){
            $foundResource = true;
            $idCountdown = (int) $this->config[ 'IdLength' ];
        }

        $childsResourceConfigPointer = &$this->config[ 'Resources' ];
        $resourceConfigPointer = $this->config;
        
        
        $pathParams = array();
        $collection = false;
        
        $backtrace = $this->backtrace;
        
        if( isset( $backtrace[ 0 ] ) ){
            $relativeReference = $backtrace[ 0 ]->relativeReference;
            $shortReference = $backtrace[ 0 ]->uniqId;
        }
        else{
            $relativeReference = '';
            $shortReference = '';
        }
        
        
        $itemRequestCreated = false;
        

        foreach( $resourcesArray as $r ){
            
            if( $idCountdown == 0 ){ //need to find a resource name
                
                    $numCountdown--;
                                       
                    if( isset( $childsResourceConfigPointer [ $r ] ) ){
                        
                        $collection = ( $numCountdown == 0 );

                        if( strtoupper( $childsResourceConfigPointer [ $r ][ 'IdLength' ] ) == 'UNLIMITED' ) {
                            $idCountdown = -1;    
                            $numCountdown = 0;
                        }
                        else{
                            $idCountdown = (int) $childsResourceConfigPointer [ $r ][ 'IdLength' ];
                            $numCountdown -= $idCountdown;
                        }
                        
                        $relativeReference .= '/' .$r;
                        $shortReference .=  '/' . array_search( $r, array_keys( $childsResourceConfigPointer ) ) ;
                        
                        //TENGO QUE CREAR UN ANCESTOR QUE SEA LA PROPIA LISTA
                        array_unshift( $backtrace, new \org\copperforest\oak\resources\Request( $shortReference, $relativeReference, $r ) );
                        
                        if( isset( $backtrace[ 1 ] )  ){
                            $backtrace[ 0 ]->parent = $backtrace[ 1 ];
                        }
                        
                        $resourceConfigPointer = &$childsResourceConfigPointer [ $r ];
                        $childsResourceConfigPointer = &$childsResourceConfigPointer [ $r ][ 'resources' ];
                        
                        $foundResource = true;
                        $itemRequestCreated = false;
                        
                    }
                    else if( !$foundResource ){
                        $pathParams[] = $r;
                    }
                    else{ //the firsts params can be anything
                        throw  new ex\BadRequestException();
                    }
                    

            }
            else{
                
                $relativeReference .= '/' .$r;
                $shortReference .= '/' . $r;
                
                if( !$itemRequestCreated ){
                    
                    array_unshift( $backtrace, new \org\copperforest\oak\resources\Request( $shortReference, $relativeReference, $backtrace[ 0 ]->resourceName ) );
                    
                    if( isset( $backtrace[ 1 ] )  ){
                        $backtrace[ 0 ]->parent = $backtrace[ 1 ];
                    }
                    
                    $itemRequestCreated = true;
                }
                
                $backtrace[ 0 ]->id[] = ( $resourceConfigPointer[ 'IdNumeric' ] ? (float) $r : $r );
                $backtrace[ 0 ]->uniqId = $shortReference;
                $backtrace[ 0 ]->relativeReference = $relativeReference;

                $idCountdown--;
            }
            
            
        }
    
        $configuration = new Configuration( $resourceConfigPointer );
        $configuration->pathParams = $pathParams;
        $configuration->collection = $collection;
        $configuration->backtrace = $backtrace;
        $configuration->isRoot = $isRoot;

        return $configuration;
    }
    
    public function getResponse( $accepts = array( 'application/vnd.copperforest.oak' ) )
    {
        
        list( $dataSourceClass, $dataSourceConfig, $this->goundTemplate, $this->accept ) = $this->getFactoryAndGround( $accepts );

        $dataSource = new $dataSourceClass();
        $dataSource->setConfiguration( $dataSourceConfig, $this->backtrace );

        //Aqui ya debería tener establecido el 
        if( $this->collection ){
            $response = $dataSource->fetchInstance( $this->backtrace[ 0 ]->relativeReference );
                  
            $this->collection = false;
            list( $dataSourceClass, $dataSourceConfig, , ) = $this->getFactoryAndGround();
            $this->collection = true;

            $dataSource = new $dataSourceClass();
            $dataSource->setConfiguration( $dataSourceConfig, $this->backtrace );
           
            $response->setChildsFactory( $dataSource );
            
             
        }
        else{
            $response = $dataSource->fetchInstance( $this->backtrace[ 0 ]->id );
        }


        $response->setConfiguration( $this );

        return $response;
    }

    public function getGroundTemplate( $accepts )
    {
        //list( , , $ground ) = $this->getFactoryAndGround( $accepts );
        return array( $this->goundTemplate, $this->accept );
    }


    protected function getFactoryAndGround( $accepts = array( 'application/vnd.copperforest.oak' ) )
    {

        self::$contador++;
        
        $method = ucwords( strtolower( $_SERVER[ 'REQUEST_METHOD' ] ) );
        $config = $this->config[ $this->collection ? 'Collections' : 'Items' ];
   
        if( self::$contador == 2 ){
            throw new \Exception('Contador 1');
        }
        
        
        foreach( $accepts as $a ){
            
            foreach( $config as $r ){

                if( ( $r[ 'Request' ][ 'isRoot' ] == $this->isRoot  ) && isset( $r[ 'Request' ][ $method ] ) ) {
                    
                    $grounds = $r[ 'Request' ][ $method ];
                    
                    if( isset( $grounds[ $a ] ) ){
                        return array( $r[ 'FactoryClass' ], $r[ 'FactoryConfiguration' ], $grounds[ $a ], $a );  
                    }
                    else if( $a == '*/*' ){ //entonces tendría que seleccionar el primer accept

                        list( $a, $g ) = each( $grounds );
                        return array( $r[ 'FactoryClass' ], $r[ 'FactoryConfiguration' ], $g, $a  );
                    }
                    else if( isset( $grounds[ '*/*' ] ) ){
                        return array( $r[ 'FactoryClass' ], $r[ 'FactoryConfiguration' ], $grounds[ '*/*' ], '*/*' );
                    }
                }
            }
        }
        
        throw  new ex\NotAcceptableException();
    }


    //debe tener el método build para crear la configuración de recursos
    public static function build()
    {
        //Aqui tengo que establecer a partir del archivo de configuración request.xml la configuración.
        $xml = simplexml_load_file( CF_APP_PATH . 'resources.xml' );
        
        $requests = array();
        
        if( isset( $xml->Requests->RequestSet[ 1 ] ) ){
            
            foreach( $xml->Requests->RequestSet as $g ){
                $requests[ (string) $g[ 'Name' ] ] = self::parseRequestSetTag( $g );
            }
        }
        else if( isset(  $xml->Requests->RequestSet ) ){
            $requests[ (string) $xml->Requests->RequestSet[ 'Name' ] ] = self::parseRequestSetTag( $r );
        }


        $resources = array();
        self::parseReourcesTag( $xml, $resources, $requests );
        
        
        file_put_contents( CF_BUILD_PATH . $_SERVER[ 'CF_BUILD' ] . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'oak.conf', serialize($resources) );
    }

    //OJO, tenemos que contenplar la posibilidad de que no tenga accept, con lo cual
    static function parseRequestSetTag( $r )
    {
        $request = array();
        
        $request[ 'Root' ] = isset( $r[ 'Root' ] ) && ( strtoupper( $r[ 'Root' ] ) == 'TRUE' ||  ( (int) $r[ 'Root' ] ) == 1 );
        
        if( isset( $r->Get ) ){
            $request[ 'Get' ] = array();
            
            if( isset( $r->Get->GroundTemplate[ 1 ] ) ){
                
                foreach( $r->Get->GroundTemplate as $g ){
                    $request[ 'Get' ][ (string) $g[ 'Accept' ] ] = (string) $g;
                }
            }
            else if( isset( $r->Get->GroundTemplate ) ){
                $request[ 'Get' ][ (string) $r->Get->GroundTemplate[ 'Accept' ] ] = (string) $r->Get->GroundTemplate;
            }
            else{
                $request[ 'Get' ][ '*/*' ] = null;
            }
        }
        
        if( isset( $r->Post ) ){
            $request[ 'Post' ] = array();
            
            if( isset( $r->Post->GroundTemplate[ 1 ] ) ){
                
                foreach( $r->Post->GroundTemplate as $g ){
                    $request[ 'Post' ][ (string) $g[ 'Accept' ] ] = (string) $g;   
                }
            }
            else if( isset( $r->Post->GroundTemplate ) ){
                $request[ 'Post' ][ (string) $r->Post->GroundTemplate[ 'Accept' ] ] = (string) $r->Post->GroundTemplate;
            }
            else{
                $request[ 'Post' ][ '*/*' ] = null;
            }
        }
        
        if( isset( $r->Patch ) ){
            $request[ 'Patch' ] = array();
            
            if( isset( $r->Patch->GroundTemplate[ 1 ] ) ){
                
                foreach( $r->Patch->GroundTemplate as $g ){
                    $request[ 'Patch' ][ (string) $g[ 'Accept' ] ] = (string) $g;
                }
            }
            else if( isset( $r->Patch->GroundTemplate ) ){
                $request[ 'Patch' ][ (string) $r->Patch->GroundTemplate[ 'Accept' ] ] = (string) $r->Patch->GroundTemplate;
            }
            else {
                $request[ 'Patch' ][ '*/*' ] = null;
            }

        }
        
        if( isset( $r->Put ) ){
            $request[ 'Put' ] = array();
            
            if( isset( $r->Put->GroundTemplate[ 1 ] ) ){
                
                foreach( $r->Put->GroundTemplate as $g ){
                    $request[ 'Put' ][ (string) $g[ 'Accept' ] ] = (string) $g;
                }
            }
            else if( isset( $r->Put->GroundTemplate ) ){
                $request[ 'Put' ][ (string) $r->Put->GroundTemplate[ 'Accept' ] ] = (string) $r->Put->GroundTemplate;
            }
            else {
                $request[ 'Put' ][ '*/*' ] = null;
            }
        }
        
        if( isset( $r->Delete ) ){
            $request[ 'Delete' ] = array();
            
            if( isset( $r->Delete->GroundTemplate[ 1 ] ) ){
                
                foreach( $r->Delete->GroundTemplate as $g ){
                    $request[ 'Delete' ][ (string) $g[ 'Accept' ] ] = (string) $g;
                }
            }
            else if( isset( $r->Delete->GroundTemplate ) ){
                $request[ 'Delete' ][ (string) $r->Delete->GroundTemplate[ 'Accept' ] ] = (string) $r->Delete->GroundTemplate;
            }
            else {
                $request[ 'Put' ][ '*/*' ] = null;
            }
        }
        
        
        return $request;
    }
    
    static function parseResourceArrays( $r , $requestConfig ){
        $items = array();
        
        if( isset( $r->Item[ 1 ] ) ){

            foreach( $r->Item as $f ){

                $factoryClass = null;
                if( isset( $f->ResponseFactory ) && isset( $f->ResponseFactory->FullyQualifiedName ) ){
                    $factoryClass = (string) $f->ResponseFactory->FullyQualifiedName;
                }
                else{
                    $factoryClass = '\org\copperforest\oak\resources\factories\ResponseFactory';
                }

                $items[] = array(
                    'Request' => $requestConfig[ (string) $f[ 'IfRequestSet' ] ],
                    'FactoryClass' => $factoryClass,
                    'FactoryConfiguration' => $factoryClass::parseFactory( $f ) );
            }
        }
        else if( isset( $r->Item ) ){

            $factoryClass = null;
            if( isset( $r->Item->ResponseFactory ) && isset( $r->Item->ResponseFactory->FullyQualifiedName ) ){
                $factoryClass = (string) $r->Item->ResponseFactory->FullyQualifiedName;
            }
            else{
                $factoryClass = '\org\copperforest\oak\resources\factories\ResponseFactory';
            }

            $items[] = array(
                'Request' => $requestConfig[ (string) $r->Item[ 'IfRequestSet' ] ],
                'FactoryClass' => $factoryClass,
                'FactoryConfiguration' => $factoryClass::parseFactory( $r->Item ) );
        }

        $collections = array();
        
        if( isset( $r->Collection[ 1 ] ) ){

            foreach( $r->Collection as $f ){

                $factoryClass = null;
                if( isset( $f->ResponseFactory ) && isset( $f->ResponseFactory->FullyQualifiedName ) ){
                    $factoryClass = (string) $f->ResponseFactory->FullyQualifiedName;
                }
                else{
                    $factoryClass = '\org\copperforest\oak\resources\factories\ResponseFactory';
                }

                $collections[] = array(
                    'Request' => $requestConfig[ (string) $f[ 'IfRequestSet' ] ],
                    'FactoryClass' => $factoryClass,
                    'FactoryConfiguration' => $factoryClass::parseFactory( $f ) );
            }
        }
        else if( isset( $r->Collection ) ){

            $factoryClass = null;
            if( isset( $r->Collection->ResponseFactory ) && isset( $r->Collection->ResponseFactory->FullyQualifiedName ) ){
                $factoryClass = (string) $r->Collection->ResponseFactory->FullyQualifiedName;
            }
            else{
                $factoryClass = '\org\copperforest\oak\resources\factories\ResponseFactory';
            }

            $collections[] = array(
                'Request' => $requestConfig[ (string) $r->Collection[ 'IfRequestSet' ] ],
                'FactoryClass' => $factoryClass,
                'FactoryConfiguration' => $factoryClass::parseFactory( $r->Collection ) );
        }
        
        return array( $items, $collections );
    }
    
    static function parseReourcesTag( $root, &$resources, $requestConfig ){
        
        if( isset( $root->Resources->Resource[ 1 ] ) ) {
            
            foreach( $root->Resources->Resource as $r ) {
                
                $resources[ (string) $r[ 'Name' ] ]= array(
                    'Collections' => array(),
                    'Items' => array(),
                    'Resources' => array(),
                    'IdLength' => (string) $r[ 'IdLength' ],
                    'IdNumeric'=> ( strtoupper( $r[ 'IdNumeric' ] ) == 'TRUE'  || ( (string) $r[ 'IdNumeric' ] ) == '1' ) );
                
                
                list( $resources[ (string) $r[ 'Name' ] ][ 'Items' ], $resources[ (string) $r[ 'Name' ] ][ 'Collections'] ) = self::parseResourceArrays( $r, $requestConfig );
                
                if( isset( $r->Resources ) ){
                    self::parseReourcesTag( $r, $resources[ (string) $r[ 'Name' ] ][ 'Resources' ], $requestConfig );
                }
                
            }
        }
        else if( isset( $root->Resources->Resource ) ) {
            
            $resources[ (string) $root->Resources->Resource[ 'Name' ] ]= array(
                'Collections' => array(),
                'Items' => array(),
                'Resources' => array(),
                'IdLength' => (string) $root->Resources->Resource[ 'IdLength' ],
                'IdNumeric'=> ( strtoupper( $root->Resources->Resource[ 'IdNumeric' ] ) == 'TRUE' )  );

            list( $resources[ (string) $root->Resources->Resource[ 'Name' ] ][ 'Items' ], $resources[  (string) $root->Resources->Resource[ 'Name' ] ][ 'Collections'] ) = self::parseResourceArrays( $root->Resources->Resource, $requestConfig );


            if( isset( $root->Resources->Resource->Resources ) ){
                self::parseReourcesTag( $root->Resources->Resource, $resources[ (string) $root->Resources->Resource[ 'Name' ] ][ 'Resources' ], $requestConfig );
            }
        }
    }

}

